package org.hdl.anima.handler;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.hdl.anima.Application;
import org.hdl.anima.common.module.BasicModule;
import org.hdl.anima.filter.DefaultFilterChain;
import org.hdl.anima.filter.FilterChain;
import org.hdl.anima.filter.HandlerFilter;
import org.hdl.anima.filter.TimeoutFilter;
import org.hdl.anima.fronent.DefaultResponseCallBack;
import org.hdl.anima.message.ErrorResponse;
import org.hdl.anima.message.IRequest;
import org.hdl.anima.message.IResponse;
import org.hdl.anima.message.MessageIdWrap;
import org.hdl.anima.message.ResponseCallback;
import org.hdl.anima.route.Router;
import org.hdl.anima.session.ISession;
import org.hdl.anima.transport.RemoteException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author qiuhd
 * @since  2014-8-1
 * @version V1.0.0
 */
public class ServiceHandlers extends BasicModule{
	
	private static final Logger logger = LoggerFactory.getLogger(ServiceHandlers.class);
	private Map<Integer, Handler> serverHandlers = null;
	private List<HandlerFilter> beforeFilters;
	private Router router;
	
	public ServiceHandlers(String moduleName) {
		super(moduleName);
	}

	@Override
	public void initialize(Application application) {
		super.initialize(application);
		beforeFilters = initBeforeFilter();
		this.router = application.getMoulde(Router.class);
	}
	
	private List<HandlerFilter> initBeforeFilter(){
		List<HandlerFilter> filters = new ArrayList<HandlerFilter>();
		filters.addAll(this.application.getBefore());
		filters.add(new TimeoutFilter());
		return filters;
	}
	
	private List<HandlerFilter> initAfeterFilter(){
		List<HandlerFilter> filters = new ArrayList<HandlerFilter>();
		filters.addAll(this.application.getAfter());
		return filters;
	}
	
	public void process(Object message,ISession session) throws Exception {
		if (message instanceof IRequest) {
			IRequest request = (IRequest) message;
			ResponseCallback callback = null;
			if (request.isTwoWay()) {
				callback = new DefaultResponseCallBack(request, session);
			}
			beforeFilter(session, request, callback);
			if (callback != null && !callback.isDone() && request.isTwoWay()) {
				IResponse reponse = new ErrorResponse("Not Response");
				reponse.setRequest(request);
				session.send(reponse);
				logger.error(
						"Failed to handle message,cause:the request must be have response value.Maybe foget to invoke done method.",
						request.getId());
			}
		} else if (message instanceof MessageIdWrap) {
			MessageIdWrap wrap = (MessageIdWrap) message;
			router.route(wrap.getMsgId(), wrap.getContent(), session);
		} else {
			logger.error("Failed to handle request,request class type not math {}",message.getClass().getName());
		}
	}
	
	public void doHandle(IRequest request,ISession session,ResponseCallback callback) throws Exception{
		int handlerId = request.getId();
		Handler handler = serverHandlers.get(handlerId);
		if (handler != null) {
			try {
				handler.handle(request, session, callback);
			}catch(Exception e) {
				throw new Exception("Failed to handle request,handler class name:" + handler.getClass().getName(),e);
			}
		} else {
			logger.error("Failed to handle message.cause Could not found the service handler,handler id {}",request.getId());
		}
	}

	@Override
	public void start() throws IllegalStateException {
		try {
			serverHandlers =  ServiceHanlderHelper.loadFromStaticXmlInfo(this.application);
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}
	}
	
	private void beforeFilter(ISession session,IRequest request,ResponseCallback callback) throws RemoteException {
		try {
			FilterChain beforeFilterChain = builderFilterChain(beforeFilters);
			beforeFilterChain.doFilter(session, request, callback);
		}catch(Exception e) {
			logger.error("Failed to handle request id {}",request.getId(),e);
			if (request.isTwoWay()) {
				IResponse reponse = new ErrorResponse("Failed to handle request id "+ request.getId() +" cause:" + e.getMessage());
				reponse.setRequest(request);
				session.send(reponse);
			}
			return ;
		}
	}
	
	private FilterChain builderFilterChain(List<HandlerFilter> filters) {
		return new DefaultFilterChain(this, filters);
	}

	@Override
	public void stop() {
		
	}

	@Override
	public void destroy() {
		if (serverHandlers != null) {
			serverHandlers.clear();
			serverHandlers = null;
		}
		
		if(beforeFilters != null) {
			beforeFilters.clear();
			beforeFilters = null;
		}
	}
}

